﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using MyBasicLib.Collections.Generic;
using MyBasicLib.Utilities.Log;
using MyBasicLib.Utilities;

namespace MyBasicLib.Data.ORMLiteNew
{
    internal static class DbQueryableInternalExtensions
    {
      //  private static readonly ILogger log = IOC.DefaultContainer.Resolve<ILogger>();

        public static IDbQueryable<TElement> CreateQuery<TElement>(this IDbQueryable q)
        {
            return new DbQueryable<TElement>(  q.DbProvider, q.DbExpressions);
        }

        //public static IDbCommand CreateCommand(this IDbQueryable  q)
        //{
        //    IDbCommand cmd = (q as DbQueryProvider).CreateCommand(q);
        //    return cmd;
        //}
        public static TElement Execute<TElement>(this IDbQueryable<TElement> q)
        { 
            return q.DbProvider.Execute<TElement>(q);
        }

        public static IList<TElement> ExecuteList<TElement>(this IDbQueryable<TElement> q)
        { 
            return q.DbProvider.ExecuteList<TElement>(q);
        }

   
    }

    /// <summary>
    /// 
    /// </summary>
    public static class DbQueryableExtensions
    {
        #region Delegates

        public delegate T Compiled<T>(params object[] ps);

        #endregion
     public static IDbCommand GetDbCommand(this IDbQueryable q)
        {
            IDbCommand cmd = (q.DbProvider as DbQueryProvider).CreateCommand(q);
            return cmd;
        }
   

        public static Compiled<TResult> Compile<TResult>(this IDbQueryable<TResult> q)
        {
            return null;
        }

        //public static bool All<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        //{
        //    var newq = source.CreateQuery<bool>();
        //    newq.DbExpressions.Add(new AllDbExpression(predicate));
        //    var ret = newq.Execute();
        //    return ret;
        //}
        //public static bool Any<TSource>(this IDbQueryable<TSource> source)
        //{

        //    return Any(source, null);
        //}

        //public static bool Any<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        //{
        //    var newq = source.CreateQuery<bool>();
        //    newq.DbExpressions.Add(new AnyDbExpression(predicate));
        //    var ret = newq.Execute();
        //    return ret;
        //}
        //public static double Average(this IDbQueryable<double> source);
        //public static decimal? Average(this IDbQueryable<decimal?> source);
        //public static double? Average(this IDbQueryable<double?> source);
        //public static double? Average(this IDbQueryable<int?> source);
        //public static decimal Average(this IDbQueryable<decimal> source);
        //public static double? Average(this IDbQueryable<long?> source);
        //public static double Average(this IDbQueryable<int> source);
        //public static double Average(this IDbQueryable<long> source);
        //public static float? Average(this IDbQueryable<float?> source);
        //public static float Average(this IDbQueryable<float> source);
        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int?>> selector);
        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long?>> selector);
        //public static decimal Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal>> selector);
        //public static double? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double?>> selector);
        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double>> selector);
        //public static decimal? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector);
        //public static float? Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float?>> selector);
        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int>> selector);
        //public static double Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long>> selector);
        //public static float Average<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float>> selector);

        //public static IDbQueryable<TResult> Cast<TResult>(this IDbQueryable source);

        public static int Count<TSource>(this IDbQueryable<TSource> source)
        {
            return Count(source, null);
        }

        public static int Count<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            IDbQueryable<int> newq = source.CreateQuery<int>();

            newq.DbExpressions.Add(new CountDbExpression(predicate));
            int ret = newq.Execute();
            return ret;
        }

        public static IDbQueryable<TSource> DefaultIfEmpty<TSource>(this IDbQueryable<TSource> source)
        {
            source.DbExpressions.Add(new DefaultIfEmptyDbExpression());
            return source;
        }

        //public static IDbQueryable<TSource> DefaultIfEmpty<TSource>(this IDbQueryable<TSource> source, TSource defaultValue);
        public static IDbQueryable<TSource> Distinct<TSource>(this IDbQueryable<TSource> source)
        {
            source.DbExpressions.Add(new DistinctDbExpression());
            return source;
        }

        //public static IDbQueryable<TSource> Except<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2);

        public static TSource First<TSource>(this IDbQueryable<TSource> source)
        {
            return source.First(null);
        }

        public static TSource First<TSource>(this IDbQueryable<TSource> source,
                                             Expression<Func<TSource, bool>> predicate)
        {
            IDbQueryable<TSource> newq = source.CreateQuery<TSource>();
            newq.DbExpressions.Add(new FirstDbExpression(predicate));
            TSource ret = newq.Execute();
            return ret;
        }

        //public static TSource FirstOrDefault<TSource>(this IDbQueryable<TSource> source);
        //public static TSource FirstOrDefault<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

        //private static Expression GetSourceExpression<TSource>(IEnumerable<TSource> source);

        //todo 
        //public static IDbQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
        //public static IDbQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey> comparer);
        //public static IDbQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector);
        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector);
        //public static IDbQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, IEqualityComparer<TKey> comparer);
        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector);
        //public static IDbQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector, IEqualityComparer<TKey> comparer); 
        public static IDbQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IDbQueryable<TOuter> outer,
                                                                                     IDbQueryable<TInner> inner,
                                                                                     Expression<Func<TOuter, TKey>>
                                                                                         outerKeySelector,
                                                                                     Expression<Func<TInner, TKey>>
                                                                                         innerKeySelector,
                                                                                     Expression
                                                                                         <
                                                                                         Func
                                                                                         <TOuter, IDbQueryable<TInner>,
                                                                                         TResult>> resultSelector)
        {
            IDbQueryable<TResult> newq = outer.CreateQuery<TResult>();
            newq.DbExpressions.Add(new GroupJoinDbExpression
                                       {
                                           Inner = inner,
                                           OuterKeySelector = outerKeySelector,
                                           InnerKeySelector = innerKeySelector,
                                           ResultSelector = resultSelector
                                       });
            return newq;
        }

        //public static IDbQueryable<TSource> Intersect<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2);
        //public static IDbQueryable<TSource> Intersect<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2, IEqualityComparer<TSource> comparer);

        //todo
        public static IDbQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this IDbQueryable<TOuter> outer,
                                                                                IDbQueryable<TInner> inner,
                                                                                Expression<Func<TOuter, TKey>>
                                                                                    outerKeySelector,
                                                                                Expression<Func<TInner, TKey>>
                                                                                    innerKeySelector,
                                                                                Expression
                                                                                    <Func<TOuter, TInner, TResult>>
                                                                                    resultSelector)
        {
            IDbQueryable<TResult> newq = outer.CreateQuery<TResult>();
            newq.DbExpressions.Add(new JoinDbExpression
                                       {
                                           Inner = inner,
                                           OuterKeySelector = outerKeySelector,
                                           InnerKeySelector = innerKeySelector,
                                           ResultSelector = resultSelector
                                       });
            return newq;
        }


        //public static long LongCount<TSource>(this IDbQueryable<TSource> source);
        //public static long LongCount<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

        //public static TSource Max<TSource>(this IDbQueryable<TSource> source);
        //public static TResult Max<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TResult>> selector);
        //public static TSource Min<TSource>(this IDbQueryable<TSource> source);
        //public static TResult Min<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, TResult>> selector);

        public static IDbQueryable<TSource> OrderBy<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                   Expression<Func<TSource, TKey>> keySelector)
        {
            source.DbExpressions.Add(new OrderByDbExpression(keySelector));
            return source;
        }

        public static IDbQueryable<TSource> OrderBy<TSource>(this IDbQueryable<TSource> source,
                                                             string key)
        {
            source.DbExpressions.Add(new OrderByDbExpression(Expression.Constant(key)));
            return source;
        }

        public static IDbQueryable<TSource> OrderByDescending<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                             Expression<Func<TSource, TKey>> keySelector)
        {
            source.DbExpressions.Add(new OrderByDescendingDbExpression(keySelector));
            return source;
        }

        public static IDbQueryable<TSource> OrderByDescending<TSource>(
            this IDbQueryable<TSource> source, string key)
        {
            source.DbExpressions.Add(new OrderByDescendingDbExpression(Expression.Constant(key)));
            return source;
        }

        //public static IDbQueryable<TSource> Reverse<TSource>(this IDbQueryable<TSource> source);

        public static IDbQueryable<TResult> Select<TSource, TResult>(this IDbQueryable<TSource> source,
                                                                     Expression<Func<TSource, TResult>> selector)
        {
            IDbQueryable<TResult> newq = source.CreateQuery<TResult>();
            newq.DbExpressions.Add(new SelectDbExpression(selector));

            return newq;
        }

        //todo
        //public static IDbQueryable<TResult> SelectMany<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, IEnumerable<TResult>>> selector)
        //{
        //    var newq = source.CreateQuery<TResult>();
        //    newq.DbExpressions.Add(new SelectManyDbExpression()
        //    {

        //    });
        //    return newq;

        //}
        //public static IDbQueryable<TResult> SelectMany<TSource, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, int, IEnumerable<TResult>>> selector);
        public static IDbQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDbQueryable<TSource> source,
                                                                                      Expression
                                                                                          <
                                                                                          Func
                                                                                          <TSource,
                                                                                          IDbQueryable<TCollection>>>
                                                                                          collectionSelector,
                                                                                      Expression
                                                                                          <
                                                                                          Func
                                                                                          <TSource, TCollection, TResult
                                                                                          >> resultSelector)
        {
            IDbQueryable<TResult> newq = source.CreateQuery<TResult>();
            newq.DbExpressions.Add(new SelectManyDbExpression(resultSelector)
                                       {
                                       });

            return newq;
        }

        //public static IDbQueryable<TResult> SelectMany<TSource, TCollection, TResult>(this IDbQueryable<TSource> source, Expression<Func<TSource, int, IEnumerable<TCollection>>> collectionSelector, Expression<Func<TSource, TCollection, TResult>> resultSelector);


        public static TSource Single<TSource>(this IDbQueryable<TSource> source)
        {
            return source.Single(null);
        }

        public static TSource Single<TSource>(this IDbQueryable<TSource> source,
                                              Expression<Func<TSource, bool>> predicate)
        {
            IDbQueryable<TSource> newq = source.CreateQuery<TSource>();
            newq.DbExpressions.Add(new SingleDbExpression(predicate));
            TSource ret = newq.Execute();
            return ret;
        }

        //public static TSource SingleOrDefault<TSource>(this IDbQueryable<TSource> source);
        //public static TSource SingleOrDefault<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

        public static IDbQueryable<TSource> Skip<TSource>(this IDbQueryable<TSource> source, int count)
        {
            source.DbExpressions.Add(new SkipDbExpression(count));

            return source;
        }

        //public static decimal Sum(this IDbQueryable<decimal> source);
        //public static double Sum(this IDbQueryable<double> source);
        //public static int Sum(this IDbQueryable<int> source);
        //public static int? Sum(this IDbQueryable<int?> source);
        //public static long Sum(this IDbQueryable<long> source);
        //public static decimal? Sum(this IDbQueryable<decimal?> source);
        //public static double? Sum(this IDbQueryable<double?> source);
        //public static long? Sum(this IDbQueryable<long?> source);
        //public static float? Sum(this IDbQueryable<float?> source);
        //public static float Sum(this IDbQueryable<float> source);
        //public static double Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double>> selector);
        //public static decimal? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal?>> selector);
        //public static double? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, double?>> selector);
        //public static int? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int?>> selector);
        //public static long? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long?>> selector);
        //public static float? Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float?>> selector);
        //public static decimal Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, decimal>> selector);
        //public static int Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, int>> selector);
        //public static long Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, long>> selector);
        //public static float Sum<TSource>(this IDbQueryable<TSource> source, Expression<Func<TSource, float>> selector);

        public static IDbQueryable<TSource> Take<TSource>(this IDbQueryable<TSource> source, int count)
        {
            source.DbExpressions.Add(new TakeDbExpression(count));

            return source;
        }

        public static IDbQueryable<TSource> ThenBy<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                  Expression<Func<TSource, TKey>> keySelector)
        {
            source.DbExpressions.Add(new ThenByDbExpression(keySelector));
            return source;
        }

        public static IDbQueryable<TSource> ThenByDescending<TSource, TKey>(this IDbQueryable<TSource> source,
                                                                            Expression<Func<TSource, TKey>> keySelector)
        {
            source.DbExpressions.Add(new ThenByDescendingDbExpression(keySelector));
            return source;
        }

        //public static IDbQueryable<TSource> Union<TSource>(this IDbQueryable<TSource> source1, IDbQueryable<TSource> source2)
        //{
        //    return source1.CreateQuery < TSource >(). source1;
        //}
        //public static IDbQueryable<TSource> Union<TSource>(this IDbQueryable<TSource> source1, IEnumerable<TSource> source2, IEqualityComparer<TSource> comparer);

        public static IDbQueryable<TSource> Where<TSource>(this IDbQueryable<TSource> source,
                                                           Expression<Func<TSource, bool>> predicate)
        {
            source.DbExpressions.Add(new WhereDbExpression(predicate));
            return source;
        }

        //public static IDbQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IDbQueryable<TFirst> source1, IEnumerable<TSecond> source2, Expression<Func<TFirst, TSecond, TResult>> resultSelector);


        public static TSource[] ToArray<TSource>(this IDbQueryable<TSource> source)
        {
            return ToList(source).ToArray();
        }

        public static TElement[] ToArray<TElement>(this IDbQueryable<TElement> source, int index,
                                                   int pageSize)
        {
            if (index < 1)
                index = 1;
            return source.Skip((index - 1)*pageSize).Take(pageSize).ToArray();
        }

        public static List<TElement> ToList<TElement>(this IDbQueryable<TElement> source)
        {
            IEnumerable<TElement> data = source.ExecuteList();
            if (data == null) return new List<TElement>(0);
            return new List<TElement>(data);
        }

        public static List<TElement> ToList<TElement>(this IDbQueryable<TElement> source, int index,
                                                      int pageSize)
        {
            if (index < 1)
                index = 1;
            return source.Skip((index - 1)*pageSize).Take(pageSize).ToList();
        }

        public static PagedList<TElement> ToPagedList<TElement>(this IDbQueryable<TElement> source, int index,
                                                                int pageSize)
        {
            int count = source.Count();
            int pages = count/pageSize;
            if (count%pageSize > 0) pages++;
            if (index > pages)
            {
                index = pages;
            }
            if (index < 1)
                index = 1;
            List<TElement> ds = source.ToList(index, pageSize);
            return new PagedList<TElement>(ds, index, pageSize, count);
        }

        //         #region Helper方法
        // 
        //         static PropertyInfo GetPropertyInfo<TSource>(string key)
        //         {
        //             var pi = TypeDataCache.GetTypeData<TSource>().GetPropertyInfo(key);
        // 
        //             if (pi == null)
        //                 throw new Exception("找不到该列：" + key);
        //             return pi;
        //         }
        //         #endregion
    }
}